 

package typistcal.userinterface;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.LinkedList;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.Timer;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import typistcal.commandlogic.CommandProcessor;
import typistcal.commandlogic.TimeUtility;

//@author A0096911Y
public class UserInterface extends JFrame implements KeyListener {

	private static final String WELCOME_MESSAGE = "Welcome!";
	private static final String NEWLINE = System.getProperty("line.separator");
	private static final int ENTER_KEY = 10;
	private static final int WIDTH_TABLE_TIME = 150;
	private static final int WIDTH_TABLE_LABEL = 200;
	private static final String[] COLUMN_NAME_SHOW = {"Label", "Start", "End"};
	private static final String[] LABEL_HELP = {"Command", "Priority Express", "Function"};
	private static final int WIDTH_TABLE_COMMAND = 50;
	private static final int WIDTH_TABLE_PRIORITY = 40;
	private static final int WIDTH_TABLE_FUNCTION = 200;
	private static final Font FONT_LABEL = new Font("Console", Font.BOLD, 12);
	private static final Font FONT_DISPLAY = new Font("Console", Font.PLAIN, 16);
	private static JLabel clockArea;
	private static JTextPane displayArea;
	private static JTextPane commandArea;
	private static JTextPane interactiveArea;
    private static JTextField typingArea;
    private static JLabel commandLabel;
    private static JLabel interactiveLabel;
    private static SimpleAttributeSet styleDefault = new SimpleAttributeSet();
    private static SimpleAttributeSet styleBlue = new SimpleAttributeSet();
    private static SimpleAttributeSet styleRed = new SimpleAttributeSet();
    private static SimpleAttributeSet styleHighlight = new SimpleAttributeSet();
    private static UserInterfaceHelper uih = new UserInterfaceHelper();

    public static void createAndShowGUI() {
        //Create and set up the window.
        UserInterface frame = new UserInterface("TypistCal");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        
        //Set up the content pane.
        assignAttributeSets();
        frame.addComponentsToPane(frame.getContentPane());
        displayToDisplayArea(WELCOME_MESSAGE);
        displayWelcomePage();
        uih.start();
        
        Timer timer = new Timer(1000, new ActionListener() {
        	@Override
        	public void actionPerformed(ActionEvent e) {
        		clockTick();
        	}
        });
        timer.setRepeats(true);
        timer.setCoalesce(true);
        timer.start();
        
        //Display the window.
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    
    private static void assignAttributeSets() {
    	//styleDefault
    	StyleConstants.setForeground(styleDefault, Color.BLACK);
    	
    	//styleUserInput
    	StyleConstants.setForeground(styleBlue, Color.BLUE);
    	
    	//styleSystemOutput
    	StyleConstants.setForeground(styleRed, Color.RED);
    	
    	//styleHighlight
    	StyleConstants.setBackground(styleHighlight, Color.YELLOW);
    }
    
    private void addComponentsToPane(Container pane) {
    	
    	clockArea = new JLabel();
        //clockArea.setOpaque(true);
        clockArea.setFont(FONT_DISPLAY);
        clockTick();
    	
        typingArea = new JTextField();
        typingArea.addKeyListener(this);
        typingArea.setFont(FONT_DISPLAY);
        
        displayArea = new JTextPane();
        displayArea.setEditable(false);
        displayArea.setFont(FONT_DISPLAY); 
        JScrollPane displayAreaScrollPane = new JScrollPane(displayArea);
        displayAreaScrollPane.setPreferredSize(new Dimension(700, 550));
        
        commandArea = new JTextPane();
        commandArea.setEditable(false);
        commandArea.setFont(FONT_DISPLAY); 
        JScrollPane commandAreaScrollPane = new JScrollPane(commandArea);
        commandAreaScrollPane.setPreferredSize(new Dimension(200,350));
        
        commandLabel = new JLabel();
        commandLabel.setFont(FONT_LABEL);
        commandLabel.setText("Previous commands: ");
        
        interactiveArea = new JTextPane();
        interactiveArea.setEditable(false);
        interactiveArea.setFont(FONT_DISPLAY);
        JScrollPane interactiveAreaScrollPane = new JScrollPane(interactiveArea);
        interactiveAreaScrollPane.setPreferredSize(new Dimension(200, 200));
        
        interactiveLabel = new JLabel();
        interactiveLabel.setFont(FONT_LABEL);
        interactiveLabel.setText("Interaction:");
        
        pane.setLayout(new GridBagLayout());
        GridBagConstraints ckArea = new GridBagConstraints();
        GridBagConstraints cArea = new GridBagConstraints();
        GridBagConstraints dArea = new GridBagConstraints();
        GridBagConstraints tArea = new GridBagConstraints();
        GridBagConstraints iArea = new GridBagConstraints();
        GridBagConstraints iLabel = new GridBagConstraints();
        GridBagConstraints cLabel = new GridBagConstraints();

        ckArea.fill = GridBagConstraints.HORIZONTAL;
        ckArea.weightx = 0;
        ckArea.weighty = 0;
        ckArea.gridx = 0;
        ckArea.gridy = 0;
        ckArea.anchor = GridBagConstraints.FIRST_LINE_START;
        pane.add(clockArea, ckArea);
        
        cArea.fill = GridBagConstraints.BOTH;
        cArea.weightx = 1;
        cArea.weighty = 1;
        cArea.gridx = 1;
        cArea.gridy = 3;
        cArea.anchor = GridBagConstraints.LINE_END;
        pane.add(commandAreaScrollPane, cArea);
        
        cLabel.fill = GridBagConstraints.HORIZONTAL;
        cLabel.weightx = 0;
        cLabel.weighty = 0;
        cLabel.gridx = 1;
        cLabel.gridy = 2;
        cLabel.anchor = GridBagConstraints.LINE_END;
        pane.add(commandLabel, cLabel);
        
        dArea.fill = GridBagConstraints.BOTH;
        dArea.weightx = 1;
        dArea.weighty = 1;
        dArea.gridx = 0;
        dArea.gridy = 1;
        dArea.gridheight = 3;
        dArea.anchor = GridBagConstraints.LINE_START;
        pane.add(displayAreaScrollPane, dArea);
        
        tArea.fill = GridBagConstraints.HORIZONTAL;
        tArea.weightx = 0;
        tArea.weighty = 0;
        tArea.gridx = 0;
        tArea.gridy = 4;
        tArea.gridwidth = 2;
        tArea.anchor = GridBagConstraints.PAGE_END;
        pane.add(typingArea, tArea);
        
        iArea.fill = GridBagConstraints.BOTH;
        iArea.weightx = 1;
        iArea.weighty = 1;
        iArea.gridx = 1;
        iArea.gridy = 1;
        iArea.anchor = GridBagConstraints.FIRST_LINE_END;
        pane.add(interactiveAreaScrollPane, iArea);
        
        iLabel.fill = GridBagConstraints.HORIZONTAL;
        iLabel.weightx = 0;
        iLabel.weighty = 0;
        iLabel.gridx = 1;
        iLabel.gridy = 0;
        iLabel.anchor = GridBagConstraints.LINE_END;
        pane.add(interactiveLabel, iLabel);

    }
    
    private static void clockTick() {
    	clockArea.setText(TimeUtility.getToday("EEEE, d MMMM yyyy, HH:mm:ss"));
    }
    
    public UserInterface(String name) {
        super(name);
    }
    
    @Override
    public void keyPressed(KeyEvent e) {
        obtainInputFromUser(e);
    }
    
    //@author ide
    @Override
	public void keyTyped(KeyEvent e) {
    	;
	}

	@Override
	public void keyReleased(KeyEvent e) {
		;
	}
	
	//@author A0096911Y
	private void obtainInputFromUser(KeyEvent e){
        
    	int keyCode = e.getKeyCode();
    	
    	if (keyCode == ENTER_KEY) {
    		String userInput;
    		JTextField textfield = (JTextField) e.getSource();
    		userInput = textfield.getText();
    		textfield.setText("");
    	
    		uih.storeInbuffer(userInput);
    	}
    }

	private static void displayWelcomePage() {
		displayToDisplayArea("Events today: ");
		executeUserInput("show today");
	}

	public static void executeUntilExit() {
		String userInput = uih.getFeedback().trim();
		
		
		while ( !userInput.equalsIgnoreCase("exit") ) {
			displayToCommandArea(userInput);
			if( userInput.isEmpty() ){
				userInput = uih.getFeedback();
				continue;
			}
			if ( userInput.startsWith("help") ) {
				displayHelpScreen(userInput);
				userInput = uih.getFeedback();
				continue;
			}
			if ( userInput.equalsIgnoreCase("clear") ) {
				clearDisplay(displayArea);
				userInput = uih.getFeedback();
				continue;
			}

			executeUserInput(userInput);
			displayToDisplayArea(NEWLINE);
			clearDisplay(interactiveArea);
			userInput = uih.getFeedback();
		}
		//exit code for GUI
		exitGUI();
	}
	
	//@author A0105620X
	private static void executeUserInput(String input) {
		CommandProcessor cp = new CommandProcessor();
		cp.createNewCommand(input);
		StringBuffer feedback = cp.executeCurrCommand();
		
		displayToDisplayArea(feedback.toString());
	}
	
	//@author A0096911Y
	private static void clearDisplay(JTextPane pane) {
		pane.setText("");
	}

	private static void exitGUI() {
		System.exit(0);;
	}

	public static void displayToDisplayArea(String lineOfText) {        
        displayToDisplayArea(lineOfText, styleDefault);
    }
	
	public static void displayToDisplayArea(String lineOfText, SimpleAttributeSet attr) {		
		StyledDocument doc = displayArea.getStyledDocument();
		lineOfText = lineOfText + NEWLINE;
		try {
			doc.insertString(doc.getLength(), lineOfText, attr);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		displayArea.setCaretPosition(displayArea.getDocument().getLength());
	}
	
	public static void displayToInteractiveArea(String lineOfText) {
		displayToInteractiveArea(lineOfText, styleBlue);
	}
	
	public static void displayToInteractiveArea(String lineOfText, SimpleAttributeSet attr) {
		lineOfText += NEWLINE;
		interactiveArea.setText("");
		StyledDocument doc = interactiveArea.getStyledDocument();
		
		try {
			doc.insertString(0, lineOfText, attr);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		interactiveArea.setCaretPosition(interactiveArea.getDocument().getLength());
	}
	
	private static void displayToCommandArea(String lineOfText, SimpleAttributeSet attr) {
		lineOfText += NEWLINE;
		StyledDocument doc = commandArea.getStyledDocument();
		doc.setCharacterAttributes(0, doc.getLength(), styleDefault, true);
		try {
			doc.insertString(doc.getLength(), lineOfText, attr);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		commandArea.setCaretPosition(commandArea.getDocument().getLength());
	}
	
	private static void displayToCommandArea(String lineOfText) {
		displayToCommandArea(lineOfText, styleRed);
	}
    
    public static String displayAndAskForFeedback(String textForUser) {
    	if (textForUser != null) {
    		displayToInteractiveArea(textForUser);
    	}
    	String feedback = uih.getFeedback();
    	if ( !feedback.equals("x") && !feedback.equals("") ) {
    		displayToCommandArea(feedback);
    	}
    	
    	return feedback;
    }
    
    public static void displayInTable(String[][] text) {
    	if ( text.length != 0 ) {
	    	StyledDocument doc = displayArea.getStyledDocument();
 
	    	try {
	    		doc.insertString(doc.getLength(), "\n", getShowTableStyle(text));
	    	} catch (BadLocationException e) {
	    		e.printStackTrace();
	    	}
			displayArea.setCaretPosition(displayArea.getDocument().getLength());
    	}
    }
    
    private static SimpleAttributeSet getShowTableStyle(String[][] text) {
    	SimpleAttributeSet attr = new SimpleAttributeSet();
    	int rowsNeeded = text.length;
    	
    	StyleConstants.setComponent(attr, getShowTableComponent(rowsNeeded, text));
    	
    	return attr;
    }
    
    private static JScrollPane getShowTableComponent(int rowsNeeded, String[][] text) {
    	JTable table = new JTable(getTableModel(COLUMN_NAME_SHOW, rowsNeeded, text));
    	Dimension d = table.getPreferredSize();
        d.width = 300;
        table.setPreferredScrollableViewportSize(d);
        table.setAlignmentY(LEFT_ALIGNMENT);
        TableColumn tc = table.getColumn(COLUMN_NAME_SHOW[1]);
        tc.setMaxWidth(WIDTH_TABLE_TIME);
        tc.setPreferredWidth(WIDTH_TABLE_TIME);
        tc = table.getColumn(COLUMN_NAME_SHOW[2]);
        tc.setMaxWidth(WIDTH_TABLE_TIME);
        tc.setPreferredWidth(WIDTH_TABLE_TIME);
        tc = table.getColumn(COLUMN_NAME_SHOW[0]);
        tc.setMinWidth(WIDTH_TABLE_LABEL);
        return new JScrollPane(table);
    }
    
    private static void displayHelpScreen(String input) {
	//    	FileOperator helpMe = new FileOperator("helpme.txt");
	//    	displayToDisplayArea(helpMe.readAll());
	    	String[] inputTokens = input.split(" ");
	    	assert(inputTokens[0].equals("help") );
	    	
	    	if ( inputTokens.length == 1 ) {
	    		HelpMessage.getHelpMessage("all");
	    	} else if ( inputTokens.length == 2 ) {
	    		HelpMessage.getHelpMessage(inputTokens[1]);
	    	} else {
	    		HelpMessage.getHelpMessage("all");
	    	}
	}
    
    public static void displayHelpTable(String[][] text) {
    	if ( text.length != 0 ) {
	    	StyledDocument doc = displayArea.getStyledDocument();
 
	    	try {
	    		doc.insertString(doc.getLength(), "\n", getHelpTableStyle(text));
	    	} catch (BadLocationException e) {
	    		e.printStackTrace();
	    	}
			displayArea.setCaretPosition(displayArea.getDocument().getLength());
    	}
    }

	private static SimpleAttributeSet getHelpTableStyle(String[][] text) {
    	SimpleAttributeSet attr = new SimpleAttributeSet();
    	int rowsNeeded = text.length;
    	
    	StyleConstants.setComponent(attr, getHelpTableComponent(rowsNeeded, text));
    	
    	return attr;
    }
    
    private static JScrollPane getHelpTableComponent(int rowsNeeded, String[][] text) {
    	JTable table = new JTable(getTableModel(LABEL_HELP, rowsNeeded, text));
    	Dimension d = table.getPreferredSize();
    	d.width = 300;
        table.setPreferredScrollableViewportSize(d);
        table.setAlignmentY(LEFT_ALIGNMENT);
        TableColumn tc = table.getColumn(LABEL_HELP[0]);
        tc.setMaxWidth(WIDTH_TABLE_COMMAND);
        tc.setPreferredWidth(WIDTH_TABLE_COMMAND);
        tc = table.getColumn(LABEL_HELP[1]);
        tc.setMaxWidth(WIDTH_TABLE_PRIORITY);
        tc.setPreferredWidth(WIDTH_TABLE_PRIORITY);
        tc = table.getColumn(LABEL_HELP[2]);
        tc.setMinWidth(WIDTH_TABLE_FUNCTION);
        
    	return new JScrollPane(table);
    }
    
    private static AbstractTableModel getTableModel(final String[] columnNames, final int i, final String[][] text) {
    	return new AbstractTableModel() {
    		
    		@Override
    		public String getColumnName(int index) {
    			//String[] columnName = {"Label", "Start", "End"};
    			
    			return columnNames[index];
    		}
    		
			@Override
			public int getColumnCount() {
				return columnNames.length;
			}

			@Override
			public int getRowCount() {
				return i;
			}

			@Override
			public Object getValueAt(int row, int column) {
				return text[row][column];	
			}
    		
    	};
    }
}

class UserInterfaceHelper extends Thread {
	
	private static LinkedList<String> inputBuffer = new LinkedList<String>(); 
	
	public UserInterfaceHelper() {}
	
	public void run() {
		waitForBuffer();
		UserInterface.executeUntilExit();
	}
	
	public synchronized void storeInbuffer(String userInput) {
		inputBuffer.push(userInput);
		this.notify();
	}
	
	public String getFeedback() {
		waitForBuffer();
		assert(!inputBuffer.isEmpty());
		return inputBuffer.pop();
	}
	
	private synchronized void waitForBuffer() {		
		try {
			if ( inputBuffer.isEmpty() ) {
				this.wait();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
